package com.dbflow5.processor;

import com.dbflow5.data.Blob;
import com.squareup.javapoet.ArrayTypeName;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.TypeName;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

/**
 * Author: andrewgrosner
 * Description: Holds the mapping between SQL data types and java classes used in the com.dbflow5.processor.
 */
public enum SQLiteHelper {

    INTEGER {
        final String sqLiteStatementMethod = "Long";

        final String sqliteStatementWrapperMethod = "Number";
    },
    REAL {
        final String sqLiteStatementMethod = "Double";
    },
    TEXT {
        final String sqLiteStatementMethod = "String";
    },
    BLOB {
        final String sqLiteStatementMethod = "Blob";
    };

    public String sqLiteStatementMethod;

    public String sqliteStatementWrapperMethod = sqLiteStatementMethod;

    private static final Map<TypeName, SQLiteHelper> sTypeMap = new HashMap<TypeName, SQLiteHelper>(){{
        put(TypeName.BYTE, INTEGER);
        put(TypeName.SHORT, INTEGER);
        put(TypeName.INT, INTEGER);
        put(TypeName.LONG, INTEGER);
        put(TypeName.FLOAT, REAL);
        put(TypeName.DOUBLE, REAL);
        put(TypeName.BOOLEAN, INTEGER);
        put(TypeName.CHAR, TEXT);
        put(ArrayTypeName.of(TypeName.BYTE), BLOB);
        put(TypeName.BYTE.box(), INTEGER);
        put(TypeName.SHORT.box(), INTEGER);
        put(TypeName.INT.box(), INTEGER);
        put(TypeName.LONG.box(), INTEGER);
        put(TypeName.FLOAT.box(), REAL);
        put(TypeName.DOUBLE.box(), REAL);
        put(TypeName.BOOLEAN.box(), INTEGER);
        put(TypeName.CHAR.box(), TEXT);
        put(ClassName.get(String.class), TEXT);
        put(ArrayTypeName.of(TypeName.BYTE.box()), BLOB);
        put(ArrayTypeName.of(TypeName.BYTE), BLOB);
        put(ClassName.get(Blob.class), BLOB);
    }};


    private static final Map<TypeName, String> sMethodMap = new HashMap<TypeName, String>(){{
        put(ArrayTypeName.of(TypeName.BYTE), "getBlob");
        put(ArrayTypeName.of(TypeName.BYTE.box()), "getBlob");
        put(TypeName.BOOLEAN, "getBoolean");
        put(TypeName.BYTE, "getInt");
        put(TypeName.BYTE.box(), "getInt");
        put(TypeName.CHAR, "getString");
        put(TypeName.CHAR.box(), "getString");
        put(TypeName.DOUBLE, "getDouble");
        put(TypeName.DOUBLE.box(), "getDouble");
        put(TypeName.FLOAT, "getFloat");
        put(TypeName.FLOAT.box(), "getFloat");
        put(TypeName.INT, "getInt");
        put(TypeName.INT.box(), "getInt");
        put(TypeName.LONG, "getLong");
        put(TypeName.LONG.box(), "getLong");
        put(TypeName.SHORT, "getShort");
        put(TypeName.SHORT.box(), "getShort");
        put(ClassName.get(String.class), "getString");
        put(ClassName.get(Blob.class), "getBlob");
    }};

    private static final HashSet<TypeName> sNumberMethodList = new HashSet<TypeName>(){{
        add(TypeName.BYTE);
        add(TypeName.DOUBLE);
        add(TypeName.FLOAT);
        add(TypeName.LONG);
        add(TypeName.SHORT);
        add(TypeName.INT);
    }};

    public static SQLiteHelper get(TypeName typeName) {
        if (sTypeMap.get(typeName) != null) {
            return sTypeMap.get(typeName);
        }
        throw new IllegalArgumentException("Cannot map "+typeName+" to a SQLite Type. If this is a " +
                "TypeConverter, ensure it maps to a primitive type.");
    }

    public static String getWrapperMethod(TypeName typeName) {
        String sqLiteHelper = get(typeName).sqliteStatementWrapperMethod;
        if (typeName == TypeName.FLOAT.box()) {
            sqLiteHelper = "Float";
        }
        return sqLiteHelper;
    }

    public static boolean containsType(TypeName typeName) {
        return sTypeMap.containsKey(typeName);
    }

    public static boolean containsMethod(TypeName typeName) {
        return sMethodMap.containsKey(typeName);
    }

    public static String getMethod(TypeName typeName) {
        return sMethodMap.get(typeName) == null? "" : sMethodMap.get(typeName);
    }

    public static boolean containsNumberMethod(TypeName typeName) {
        return sNumberMethodList.contains(typeName);
    }
}
